package com.javacoo.event.client.utils;

import java.util.List;

import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import lombok.extern.slf4j.Slf4j;

/**
 * Asm工具类
 * <li></li>
 *
 * @author: duanyong@jccfc.com
 * @since: 2021/10/18 11:23
 */
@Slf4j
public class AsmUtil extends ClassLoader{
    public static AsmUtil getInstance() {
        return AsmUtilHolder.instance;
    }
    private static class AsmUtilHolder {
        /**
         * 静态初始化器，由JVM来保证线程安全
         */
        private static AsmUtil
            instance = new AsmUtil();
    }
    /**
     * 添加注解
     * <li></li>
     * @author duanyong@jccfc.com
     * @date 2021/10/18 11:34
     * @param methods:目标方法
     * @param annotation:注解名称
     * @param clazz:当前类
     * @return: java.lang.Class<?> 添加注解后的class
     */
    public Class<?> addAnntation(List<String> methods,String annotation,Class<?> clazz){
        try {
            String className = clazz.getName();
            log.info("添加注解className:{}",className);
            ClassReader cr = new ClassReader(className);
            ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
            log.info("添加注解annotation:{}",annotation);
            AsmMethodVisitor mv = new AsmMethodVisitor(Opcodes.ASM4,cw,methods,annotation);
            log.info("添加注解mv:{}",mv);
            cr.accept(mv, 0);
            byte[] code = cw.toByteArray();
            return defineClass(null, code, 0, code.length);
        } catch (Exception e) {
            e.printStackTrace();
            log.info("类:{} 的方法:{},添加注解:{}失败,msg:{}",clazz,methods,annotation,e.getMessage());
        }
        return null;
    }

    class AsmMethodVisitor extends ClassVisitor implements Opcodes {

        private List<String> methods;
        private String annotation;

        public AsmMethodVisitor(int api, ClassVisitor cv,List<String> methods,String annotation) {
            super(api, cv);
            this.methods = methods;
            this.annotation = annotation;
        }

        @Override
        public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
            MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
            if(methods.contains(name)){
                AnnotationVisitor av1 = mv.visitAnnotation(annotation, true);
                av1.visitEnd();
            }
            return mv;
        }
    }
}
